/*
 *  hexdump.c
 *  
 *
 *  Created by Matteo Cicuttin on 07/03/10.
 *  Copyright 2010 Matteo Cicuttin. All rights reserved.
 *
 */

#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <strings.h>

#include "hexdump.h"

#define HDBUFSZ	32

struct hexdump_state_t
{
	char	*buf;
	int		bufsz, bufpos, cpl;
	FILE	*dump_stream;
};

struct hexdump_state_t *
hexdump_init(int cpl, FILE *dump_stream)
{
	struct hexdump_state_t *state;
	state = (struct hexdump_state_t *) malloc(sizeof(struct hexdump_state_t));
	if (!state)
		return NULL;
	
	state->buf = (char *) malloc (HDBUFSZ * sizeof(char));
	if (!state->buf)
	{
		free(state);
		return NULL;
	}
	
	state->bufsz = HDBUFSZ;
	state->cpl = cpl;
	state->bufpos = 0;
	state->dump_stream = dump_stream;
	
	return state;
}

void
hexdump_free(struct hexdump_state_t *state)
{
	if (state)
	{
		free(state->buf);
		free(state);
	}
}

static void
hexdump_s(struct hexdump_state_t *state)
{
	int i, c, s;
	for (i = 0; i < state->bufpos; i+= state->cpl)
	{
		c = 0;
		while ( (c < state->cpl) && (i+c < state->bufpos) )
			fprintf(state->dump_stream, "%2.2x ", state->buf[i+c++]);
		
		s = 1 + (state->cpl - c);
		while (s--)
			fprintf(state->dump_stream, "   ");
		
		c = 0;
		while ( (c < state->cpl) && (i+c < state->bufpos) )
		{
			fprintf(state->dump_stream, "%c",
					isprint(state->buf[i+c]) ? state->buf[i+c] : '.' );
			c++;
		}
			
		fprintf(state->dump_stream, "\n");
	}
}

void
hexdump(char *buf, int len, int cpl, FILE *stream)
{	
	struct hexdump_state_t state;
	state.buf = buf;
	state.bufpos = len;
	state.cpl = cpl;
	state.dump_stream = stream;
	
	hexdump_s(&state);
}

void
buffered_hexdump(char *data, int datasz, struct hexdump_state_t *state)
{
	int rem, diff;
	
	rem = state->bufsz - state->bufpos;	/* remaining space on the buffer */
	diff = rem - datasz;				/* there is enough space? */
	if (diff <= 0)
	{
		memcpy(state->buf+state->bufpos, data, rem);	/* Fill the buffer */
		state->bufpos += rem;
		hexdump_s(state); /* Flush it */
		memcpy(state->buf, data+rem, datasz-rem);	/* Put the remaining data */
		state->bufpos = datasz - rem;
	}
	else
	{
		memcpy(state->buf+state->bufpos, data, datasz);
		state->bufpos = state->bufpos + datasz;
	}
}

void
hexdump_flush(struct hexdump_state_t *state)
{
	hexdump_s(state);
	state->bufpos = 0;
}
